/*
 * Title:		grammar.g
 * Description: Contains the Lexer and Parser of the KFrog interpreter. 
 * 
 * @version		1.0 May 2009
 * @author		Rami Habas
 */
 
header {
    package kfrog;
}

class KFrogAntlrLexer extends Lexer;

options{
    k = 2;  
    charVocabulary = '\3'..'\377';
    testLiterals = false;
    exportVocab = KFrog;
}

tokens {
	NL;
    DECIMAL;
    REAL;
    /* logical operators */
    AND="AND";
    OR="OR";
    NOT="NOT";
    NOTEQ="NOT=";
    /* keywords */
    CLEAR="clearscreen";
    RESET="reset";
    PONDSIZE="pondsize";
    PONDCOLOR="pondcolor";
    FROGCOLOR="frogcolor";
    STARTDRAW="startdraw";
    STOPDRAW="stopdraw";
    SPEED="speed";
    SLEEP="sleep";
    CENTER="center";
    GOTO="goto";
    FORWARD="forward";
    TURNRIGHT="turnright";
    TURNLEFT="turnleft";
    RIGHTCURVE="rightcurve";
    LEFTCURVE="leftcurve";
}

{
    public void reportError(String s) {
        throw new RuntimeException(s);
    }

    public void reportError(RecognitionException e) {
        throw new RuntimeException(e.toString());
    }
}

protected
LETTER   : 'a'..'z' | 'A'..'Z' | '_';


protected
EXP     : ('e' | 'E') ('+' | '-')? (DIGIT)+
		;
		
protected
DIGIT    : '0'..'9';

//whitespace
WS       : (' ' | '\t' | '\f')+
            { $setType(Token.SKIP); }
         ;

//newline
NL       : ( '\n'    				 //Unix/Linux
           | ('\r' '\n') => '\r' '\n'//Windows
           | '\r')					 //Macintosh
            {newline();}
         ;
         
COMMENT : "/*" ( 
                    options {greedy=false;} : 
                    (NL)
                    | ~( '\n' | '\r' )
                )* "*/"				   { $setType(Token.SKIP); }
                 
        |  "//" (~( '\n' | '\r' ))* (NL)? { $setType(NL); }
        ;

DECIMAL_REAL
        : (DIGIT)+                     { $setType(DECIMAL); }
          (('.' (DIGIT)* (EXP)? | EXP) { $setType(REAL); })?
        | '.' (DIGIT)+ (EXP)?          { $setType(REAL); }
        ;

ID options { testLiterals = true; }
         : LETTER(LETTER|DIGIT)*
         ;
   
// Operators and separators
PLUS     : '+';
MINUS    : '-';
MULT     : '*';
DIV      : '/';
MOD      : '%';
INC      : "++";
DEC      : "--";
ASSIGN   : '=';
COMMA    : ',';
ISEQ     : "==";
PLUSEQ   : "+=";
MINUSEQ  : "-=";
MULTEQ   : "*=";
DIVEQ    : "/=";
MODEQ    : "%=";
GT		 : '>';
GTEQ     : ">=";
LT       : '<';
LTEQ     : "<=";
LPAREN   : '(';
RPAREN   : ')';
LBRACE   : '{';
RBRACE   : '}';


class KFrogAntlrParser extends Parser;

options{
    k = 1;
    buildAST = true;
    exportVocab = KFrog;
}

tokens {
    PROGRAM;
    FUNC_DEF;
    MAIN_DEF;
    ARG_LIST;

    STATEMENT;
    EXPR_LIST;
    COMPOUND_STMT;

    DECLARATION;
    TYPE;

    UPLUS;
    UMINUS;
    PREF_INC;
    PREF_DEC;
    POST_INC;
    POST_DEC;
    FUNC_CALL;
}

{
    public void reportError(String s) {
        throw new RuntimeException(s);
    }

    public void reportError(RecognitionException e) {
        throw new RuntimeException(e.toString());
    }
}


program
        : (extern_def)* EOF!
          { #program = #([PROGRAM, "program"], program); }
        ;

extern_def
        : NL!
        | (func_signature) => func_def
        | main_def
        | declaration NL!
        ;

func_def
        : func_signature func_body
          { #func_def = #([FUNC_DEF, "func_def"], func_def); }
        ;

main_def
        : "main" LPAREN! arg_list RPAREN! func_body
          { #main_def = #([MAIN_DEF, "main_def"], main_def); }
        ;
        
protected
func_signature
        : ("void" | type)? ID LPAREN! arg_list RPAREN!
        ;

arg_list
        : ( type ID (COMMA! type ID)*
          | /* Add a node for an empty arg_list */
          )
          { #arg_list = #([ARG_LIST, "arg_list"], arg_list); }
        ;

func_body
        : LBRACE! (statement)* RBRACE!
          { #func_body = #([STATEMENT, "func_body"], func_body); }
        ;

statement
        : NL!
		| frog_stmt
        | clear_stmt NL!
        | reset_stmt NL!
        | pondsize_stmt NL!
        | pondcolor_stmt NL!
        | frogcolor_stmt NL!
        | draw_stmt NL!
        | speed_stmt NL!
        | sleep_stmt NL!
        | center_stmt NL!
        | goto_stmt NL!
        | move_stmt NL!
        | curve_stmt NL!
        | declaration NL!
        | repeat_stmt
        | while_stmt
        | break_stmt
        | continue_stmt
        | return_stmt
        | if_stmt
        | expression NL!
        | LBRACE! (statement)* RBRACE!
          { #statement = #([COMPOUND_STMT, "compound_stmt"], statement); }
        ;

frog_stmt
        : "new"^ frog! statement
        ;

clear_stmt
        : CLEAR^
        ;

reset_stmt
        : RESET^
        ;

pondsize_stmt
        : PONDSIZE^ expression COMMA! expression
        ;
        
pondcolor_stmt
        : PONDCOLOR^ ID
        ;

frogcolor_stmt
        : FROGCOLOR^ ID
        ;
        
draw_stmt
        : STARTDRAW^
        | STOPDRAW^
        ;

speed_stmt
        : SPEED^ expression
        ;

sleep_stmt
        : SLEEP^ expression
        ;
        
center_stmt
        : CENTER^
        ;

goto_stmt
        : GOTO^ expression COMMA! expression
        ;
        
move_stmt
        : FORWARD^ expression
        | TURNRIGHT^ expression
        | TURNLEFT^ expression
        ;

curve_stmt
        : RIGHTCURVE^ expression COMMA! expression
        | LEFTCURVE^ expression COMMA! expression
        ;
        
if_stmt
        : "if"^ LPAREN! expression RPAREN! statement
          ( options { greedy = true; }
            : "else"! statement
          )?
        ;

while_stmt
        : "while"^ LPAREN! expression RPAREN! statement
        ;

repeat_stmt
        : "repeat"^ DECIMAL statement
        ;

expr_list
        : ( expression (COMMA! expression)*
          | /* Needed to add a node for an empty expr_list. */
          )
          { #expr_list = #([EXPR_LIST, "expr_list"], expr_list); }
        ;

break_stmt
        : "break"^ NL!
        ;

continue_stmt
        : "continue"^ NL!
        ;

return_stmt
        : "return"^ (expression)? NL!
        ;

declaration
        : type declarator_list
          { #declaration = #([DECLARATION, "declaration"], declaration); }
        ;

type
        : type_specifier
          { #type = #([TYPE, "type"], type); }
        ;

type_specifier
        : "logical" | "decimal" | "real"
        ;
        
declarator_list
        : ID initializer (COMMA! ID initializer)*
        ;

initializer
        : (ASSIGN^ expression)?
        ;

expression
        : assignment_expression
        ;

assignment_expression
        : logical_expression
          ( (ASSIGN^ | PLUSEQ^ | MINUSEQ^ | MULTEQ^ | DIVEQ^ | MODEQ^)
            assignment_expression
          )?
        ;

logical_expression
        : logical_term (OR^ logical_term)*
        ;

logical_term
        : equality_expression (AND^ equality_expression)*
        ;

equality_expression
        : relational_expression ((ISEQ^ | NOTEQ^) relational_expression)?
        ;

relational_expression
        : arithmetic_expression ((LT^|GT^|LTEQ^|GTEQ^) arithmetic_expression)?
        ;

arithmetic_expression
        : arithmetic_term ((PLUS^ | MINUS^) arithmetic_term)*
        ;

arithmetic_term
        : arithmetic_factor ((MULT^ | DIV^ | MOD^) arithmetic_factor)*
        ;

arithmetic_factor
        : inc:INC^ logical_factor { #inc.setType(PREF_INC); }
        | dec:DEC^ logical_factor { #dec.setType(PREF_DEC); }
        | p:PLUS^  logical_factor { #p.setType(UPLUS); }
        | m:MINUS^ logical_factor { #m.setType(UMINUS); }
        | logical_factor
        ;

logical_factor
        : (NOT^)? postfix_expression
        ;

postfix_expression
        : primary_expression
          ( inc:INC^ { #inc.setType(POST_INC); }
          | dec:DEC^ { #dec.setType(POST_DEC); }
          )?
        ;

primary_expression
        : ID
          ( LPAREN! expr_list RPAREN!
            {#primary_expression=#([FUNC_CALL,"func_call"],primary_expression);}
          )?
        | constant
        | LPAREN! expression RPAREN!
        ;

constant
        : DECIMAL
        | REAL
        | "true"
        | "false"
        ;

frog
        : "frog"
        ;